iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0
Mobile Development

Flutter 從零到實戰 - 30 天の學習筆記系列 第 26

[Day 26] 進階技巧 - Splash Screen 與 Welcome Screen

  • 分享至 

  • xImage
  •  

Splash Screen (啟動畫面)是一個應用程式在啟動時短時間顯示的畫面,通常該畫面用於展示和強調應用程式的品牌,同時在顯示畫面同時,應用程式也會用於進行初始化操作,載入基本的環境等。通常該畫面僅顯示數秒鐘,確保使用者不會等待太長時間。一但加載完成便會跳至主畫面,讓使用者可以開始使用應用程式的功能。

Welcome Screen (歡迎畫面) 是當使用第一次啟動應用程式時或新用戶第一次訪問應用程式時出現的畫面,用於提供簡短的介紹與導向,讓使用者可以快速的熟悉應用程式的功能。

今天我們會教各位讀者如何將兩者加入應用程式中。

Splash Screen

請建立一個 splash_screen.dart 的檔案,並參考以下程式碼:

class SplashScreen extends StatefulWidget {
  const SplashScreen({super.key});

  @override
  State<SplashScreen> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  @override
  void initState() {
    super.initState();
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
    Future.delayed(const Duration(seconds: 2), () {
      // 當 splash screen 完成後要導向的頁面,這裡先暫放 placeholder
      return Placeholder();
    });
  }
  @override
  void dispose() {
    super.dispose();
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
        overlays: SystemUiOverlay.values);
  }
  @override
  Widget build(buildContext context) {
    return // splash screen 樣式,這邊請讀者自行設計囉~
  }

我們宣告了一個 stateful widget,並於 initState 的地方使用 SystemChrome.setEnabledSystemUIMode() 方法來設定系統的 UI 模式,這邊設定為 SystemUiMode.immersive 也就是將應用程式設定為全屏模式,隱藏系統狀態欄。接著使用 Future.delayed 設定延遲兩秒的時間進入 splash screen 後要導向的頁面。

在這短暫的兩秒鐘,會暫時顯示此 widget 中 build 回傳的內容,並於兩秒鐘結束後導向應顯示的頁面。這時也就代表此 widget 的生命週期結束,調用 dispose 方法將系統 UI 顯示模式調回正常,重新顯示狀態欄。

這麼一來簡單的 splash screen 就完成拉!您也可以參考下圖來進行設計:
https://ithelp.ithome.com.tw/upload/images/20231011/2013508253vCKkJIKy.png

接著就是讓應用程式開啟時自動先進入 splash screen,請開啟 main.dart

CupertinoApp(
  // 上方皆省略
  home: const SplashScreen();
);

原先的內容先別急著刪掉!!原先 home 參數後面接的是 StreamBuilder ,用於根據當前是否登入而顯示不同頁面狀態。請把這些內容建立成 dummy_screen.dart 的檔案,再將該檔案取代 Placeholder() 工具,我們最後會畫個流程讓大家比較好理解這麼做的用意。

如此便可以讓你的應用程式在開啟時即先導向 splash screen 了。

Welcome Screen

在製作 welcome screen 前,請先執行下列指令來下載所需套件

flutter pub add smooth_page_indicator shared_preferences

因為通常 welcome screen 會有多於一頁的內容,因此使用該套件來顯示流暢的指示換頁動畫,如下圖:
https://i.imgur.com/hDLYMjZ.gif

下載完畢後,請先建立 welcome_screen_page_1.dart 表示 welcome screen 的第一頁,並參考下列程式碼:

import 'package:flutter/cupertino.dart';

class WelcomeScreenPage1 extends StatelessWidget {
  const WelcomeScreenPage1({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('我是第一頁'));
  }
}

您可以視自己需要多少 welcome screen 的 page 來建立數個檔案,並自行決定要設計的畫面內容。我會以三頁的作法作為範例。

建立完畢後請再建立一個 welcome_screen.dart 並宣告成 stateful widget,我們將用此作為外框對現在應顯示第幾頁進行切換。請參考下方程式碼:

class _WelcomeScreenState extends State<WelcomeScreen> {
  final PageController _controller = PageController();

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      child: Stack(children: [
      PageView(
        controller: _controller,
        onPageChanged: (value) {},
        children: const [
          WelcomeScreenPage1(),
          WelcomeScreenPage2(),
          WelcomeScreenPage3(),
        ],
      ),
      Padding(
        padding: const EdgeInsets.fromLTRB(0, 0, 0, 32),
        child: Container(
            alignment: Alignment.bottomCenter,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                SmoothPageIndicator(
                    controller: _controller,
                    count: 3,
                    effect: const WormEffect(
                      dotHeight: 12,
                      dotWidth: 12,
                      activeDotColor: Color.fromRGBO(255, 30, 84, 1),
                      dotColor: CupertinoColors.systemGrey4,
                    )),
              ],
            )),
  )]))}
}

接下來便是將 WelcomePage 給串接起來,並看看效果。

Shared Preferences

這是一個可以將 key 與 value 存在裝置上的套件。目前為止我們的接觸到的 state 與 provider 的值都是在應用程式關閉之後就不復存在了。而 shared preferences 套件的存在意義就是為了解決這件事情,即便應用程式關閉了,但存於 shared preferences 的 key 與 value 值仍得以保存。

我們將使用此套件來存取使用者是否是第一次進入此 APP。請參考下方程式碼:

@override
  void initState() {
    super.initState();
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
    Future.delayed(const Duration(seconds: 2), () {
      Navigator.of(context)
          .pushReplacement(CupertinoPageRoute(builder: (context) {
        return FutureBuilder(
            future: _prefs,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                final prefs = snapshot.data as SharedPreferences;
                // 檢查是否有 isFirstTime 的值
                final isFirstTime = prefs.getBool('isFirstTime') ?? true;
                if (isFirstTime) {
                  // 若為第一次使用應用程式,則導向歡迎頁面並將該值改為 false
                  prefs.setBool('isFirstTime', false);
                  return const WelcomeScreen();
                } else {
                  return const DummyScreen();
                }
              } else {
                return const DummyScreen();
              }
            });
      }));
    });
  }

https://i.imgur.com/T2IohDB.gif
目前看起來很棒,不過通常當 welcome page 滑到最末頁的時候,需要跳出一「完成」按鈕來讓使用者導向登入頁面,因此我們可以再做些許的更動。

請在該 widget 中新增一個 _onLastPage 的 state,用於判斷是否為最後一頁。

// PageView
PageView(
  contrroller: _controller,
  onPageChanged: (value) {
    setState(() {
      // 因為頁碼總共三頁,因此當頁碼為 2 時將該值設為 true,否則為 false
      _onLastPage = value == 2;
    })
  }
)

// 下方 Row 的地方
children: [
  CupertinoButton(onPressed: () {
    Navigator.of(context).pushReplacement(
        CupertinoPageRoute(builder: (context) {
      return const LoginScreen();
    }));
  }, child: const Text('跳過')),
  SmoothPageIndicator(... 省略 ...),
  _onLastPage ? 
    CupertinoButton(onPressed() { .. 跟上面跳過的 onPressed 內容一樣 .. }, child: const Text('完成'))
    : CupertinoButton(onPressed: () {
      _controller.nextPage(
        duration: const Duration(milliseconds: 500),
        curve: Cuerves.easeIn
      );
    }, child: const Text('下一頁')),
]

https://ithelp.ithome.com.tw/upload/images/20231011/20135082Mp6MEZaAM4.png

用簡單的流程圖來表示就是如此。相當容易對吧XD

今日總結

今天教了大家第一個進階技巧,也就是將 Splash Screen 與 Welcome Screen 加入到應用程式當中雖然是否加入該畫面對於應用程式本身的功能沒有任何影響,但是在使用者的體驗上能夠更加分!!

並且我們使用了 shared preferences 套件來將狀態於裝置中儲存,您也可以將 dark mode 與通知的狀態儲存於 shared preferences 中,這邊就留給讀者自己練習以及把玩拉!

最後倒數四天就結束拉~好開心好開心~~

今天的參考程式碼:https://github.com/ChungHanLin/micro_news_tutorial/tree/day26/micro_news_app


上一篇
[Day 25] 實戰新聞 APP - 擴充使用者資料 Part 2 (上傳圖片至 firebase storage)
下一篇
[Day 27] 進階技巧 - Flutter 的多國語系應用程式實現方法
系列文
Flutter 從零到實戰 - 30 天の學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言